﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Data.Services.Client;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Windows;
using PersonaLibrary.PersonaliiReference;
using PersonaLibrary.Tracer;

namespace PersonaLibrary.Data.ServiceReference
{
	internal class DataServiceContextWrapper
	{
		private readonly PersonalizationEntities _context;
		private readonly ObservableCollection<object> _addedObjects;
		private readonly ObservableCollection<object> _updatedObjects;
		private readonly ObservableCollection<object> _deletedObjects;

		public DataServiceContextWrapper()
		{
			string connectionString = ConfigurationManager.AppSettings["ServiceConnectionString"];

			_addedObjects = new ObservableCollection<object>();
			_deletedObjects = new ObservableCollection<object>();
			_updatedObjects = new ObservableCollection<object>();

			if (!string.IsNullOrEmpty(connectionString))
			{
				_context = new PersonalizationEntities(new Uri(connectionString));
			}
			else
			{
				MessageBox.Show("Ошибка конфигурации. Не возможно найти настройки для подключения к сервису. Пожалуйста обратитесь к администратору или разработчику");
				LoggerBuilder.GetLogger().Fault(new Exception("Не найден адрес сервиса персоналий"));
			}
		}

		public IEnumerable<TElement> CreateAndExecuteQuery<TElement>(string query)
		{
			return _context.Execute<TElement>(new Uri(query, UriKind.Relative));
		}

		public IEnumerable<TElement> TryCreateAndExecuteQuery<TElement>(string query, string errorMsg)
		{
			IEnumerable<TElement> result = null;
			bool successfully = false;

			try
			{
				result = CreateAndExecuteQuery<TElement>(query);
				successfully = true;
			}
			catch (Exception)
			{
				Thread.Sleep(400);
			}

			if (!successfully)
			{
				try
				{
					result = CreateAndExecuteQuery<TElement>(query);
				}
				catch (Exception)
				{
					MessageBox.Show(errorMsg);
					result = new TElement[0];
				}
			}

			return result;
		}

		public void LoadProperty(object entity, string property)
		{
			QueryOperationResponse response = _context.LoadProperty(entity, property);

			if(response != null && response.Error != null)
			{
				LoggerBuilder.GetLogger().Fault(response.Error);
			}
		}

		public void TryLoadProperty(object entity, string property, string errorMsg)
		{
			bool successfully = false;

			try
			{
				LoadProperty(entity, property);
				successfully = true;
			}
			catch (Exception)
			{
				Thread.Sleep(400);
			}

			if (!successfully)
			{
				try
				{
					LoadProperty(entity, property);
				}
				catch (Exception)
				{
					MessageBox.Show(errorMsg);
				}
			}
		}

		public IAsyncResult BeginLoadProperty(object entity, string property, AsyncCallback callback, object state)
		{
			return _context.BeginLoadProperty(entity, property, callback, state);
		}

		public void EndLoadProperty(IAsyncResult result)
		{
			_context.EndLoadProperty(result);
		}

		public void AddObject(string name, object entity)
		{
			_addedObjects.Add(entity);
			_context.AddObject(name, entity);
		}

		public void UpdateObject(object entity)
		{
			_updatedObjects.Add(entity);
			_context.UpdateObject(entity);
		}

		public void DeleteObject(object entity)
		{
			_deletedObjects.Add(entity);
			_context.DeleteObject(entity);
		}

		public void SaveChanges()
		{
			foreach (object updatedObject in _updatedObjects)
			{
				IDBEntity entity = updatedObject as IDBEntity;
				if(entity != null)
				{
					entity.UpdateDate = DateTime.Now;
					if (!string.IsNullOrEmpty(Environment.UserDomainName))
						entity.UserName = string.Concat(Environment.UserDomainName, "/", Environment.UserName);
					else
						entity.UserName = Environment.UserName;
				}
			}

			foreach (object addedObject in _addedObjects)
			{
				IDBEntity entity = addedObject as IDBEntity;
				if (entity != null)
				{
					entity.UpdateDate = DateTime.Now;
					if (!string.IsNullOrEmpty(Environment.UserDomainName))
						entity.UserName = string.Concat(Environment.UserDomainName, "/", Environment.UserName);
					else
						entity.UserName = Environment.UserName;
				}
			}
			
			_context.SaveChanges();
			RiseEntityChanged();
		}

		public void TrySaveChanges(string errorMsg)
		{
			bool successfully = false;

			try
			{
				SaveChanges();
				successfully = true;
			}
			catch (Exception)
			{
				Thread.Sleep(400);
			}

			if (!successfully)
			{
				try
				{
					SaveChanges();
				}
				catch (Exception e)
				{
					MessageBox.Show(errorMsg);
					LoggerBuilder.GetLogger().Fault(e);
				}
			}
		}

		private void RiseEntityChanged()
		{
			_addedObjects.Clear();
			_updatedObjects.Clear();
		}
	}
}
